package org.keeners.imageviewer {
	
	import flash.ui.Keyboard;
	import flash.ui.Mouse;
	
	import flash.events.KeyboardEvent;   
	import flash.events.EventDispatcher;
	import flash.events.MouseEvent;
	import flash.events.Event;
	import flash.events.ProgressEvent;
	import flash.events.IOErrorEvent;
	
	import flash.net.URLRequest;
	
	import flash.filters.BitmapFilterQuality;
	import flash.filters.DropShadowFilter;
	
	import flash.display.*;
	import flash.text.*;
		
	import flash.geom.Rectangle;
	
	import Math
	import flash.utils.Timer;
	import flash.events.TimerEvent;
	
	import flash.external.ExternalInterface;
	
	import caurina.transitions.Tweener;
	import caurina.transitions.properties.FilterShortcuts;
		
	import org.keeners.imageviewer.util.Text;
	import org.keeners.imageviewer.util.Shapes;
	
	public class ImageViewerBase extends Sprite {
		
		private var _stage:Stage;
		private var _keys:Array;
		private var _controls:ImageViewerControls;
		private var _ocr:ImageOcr;
		private var _config:Config;
		
		public var request_url:String;
		public var io_failure:Boolean;
		public var loop_callback:Function;
		
		public var loaded:Boolean;		
		public var load_progress:MovieClip;
		
		public var instructed:Boolean;
		public var information:Sprite;
		
		public var image_display:Sprite;
		public var image_cursor:Sprite;
		public var image_width:Number;
		public var image_height:Number;
		public var image_padding:Number;
		public var image_zoom_levels:Array;
		public var image_zoom_level_count:Number;
		public var image_zoom_current:Number;
		
		public var highlights_display:Sprite;
		
		public var wheel_mouse_lock_time:Number;
		
		public function ImageViewerBase(config, s, callback):void { 
			/*
				adds stage listeners, controls, cursor, and loader.
				initializes the image load.
			*/
			
			FilterShortcuts.init();
			
			_config = config;
			
			_stage = s;
			_keys = [];
			_stage.addEventListener(KeyboardEvent.KEY_DOWN, key_down);
			_stage.addEventListener(KeyboardEvent.KEY_UP, key_up);
			_stage.addEventListener(MouseEvent.MOUSE_MOVE,cursor_move);
			_stage.addEventListener(Event.RESIZE, image_maintain_bounds);
			_stage.addEventListener(Event.RESIZE, controls_position);
			_stage.addEventListener(Event.RESIZE, information_position);
			
			_controls = new ImageViewerControls(this)
			_controls.alpha = 0;
			controls_position();
			
			_ocr = new ImageOcr(_config, this);
			
			// firefox issues
			// http://stackoverflow.com/questions/1967406/
			wheel_mouse_lock_time = new Date().getTime();
			
			var init_cursor:MovieClip = new mc_icon_grabber()
			image_cursor = new mc_icon_grabber();
			image_cursor.addChild(init_cursor);
			image_cursor.mouseEnabled = true;
			image_cursor.visible = false;
			
			load_progress = new mc_loader_progress()
			load_progress.x = get_stage_width()/2;
			load_progress.y = get_stage_height()/2;
			load_progress.visible = false;
			
			request_url = config.image_url;
			loaded = false;
			instructed = false;
			io_failure = false;
			loop_callback = callback;
			
			image_zoom_levels = [];
			image_zoom_current = 0;
			image_padding = 25;
			image_display = new Sprite();
			highlights_display = new Sprite();
			
			information = new Sprite();
			
			addChild(image_display);			
			addChild(load_progress);
			addChild(information);
			addChild(_controls);
						
			load_image();
			_ocr.load_ocr(config);
		}
		
		function draw_highlights(highlights:Object):void{
			
			while (highlights_display.numChildren) {			   
				highlights_display.removeChildAt(0);
			} 
			
			if (!image_width){
				return;	
			}
			
			var t:Shapes = new Shapes();
			for (var highlight_key:String in highlights){
				var highlight_data = highlights[highlight_key]
				var box:Array = highlight_data.box;
				if (box.length != 4){ continue; }
				var w:Number = (box[1] - box[3]) * image_width;
				var h:Number = (box[2] - box[0]) * image_height;
				var padding:Number = Math.max(7, Math.min(w,h) * .25);
				var highlight:Sprite = t.highlight_rectangle(w + padding,h + padding, 0xffff00);
				highlight.x = box[3] * image_width - padding/2;
				highlight.y = box[0] * image_height - padding/2;
				highlights_display.addChild(highlight);
			}
		}
		
		function information_fade_in(msg:String=''):void{
								
			var t:Text = new Text();
			var st = t.get_standard_quickfield(0x555555,12);
			var icon:MovieClip = new mc_icon_information();
			
			if (msg == ''){
				st.text = _config.get_instructions();
			}else{
				st.text = msg;
			}
			var drop_shadow:DropShadowFilter = new DropShadowFilter();
			drop_shadow.color = 0xeeeeee;
			drop_shadow.blurX = 8;
			drop_shadow.blurY = 8;
			drop_shadow.angle = 65;
			drop_shadow.strength = 8;
			drop_shadow.alpha = .8;
			drop_shadow.distance = 0;
			drop_shadow.quality = BitmapFilterQuality.HIGH;
			information.filters = [drop_shadow];
			
			information.addChild(st);
			information.addChild(icon);
			icon.x = 0;
			icon.y = (information.height - icon.height) /2 ;
			st.x = icon.width + 3
			
			information.alpha = 0;
			information_position();
			
			Tweener.addTween(information, {alpha:1, time:.2, transition: 'linear'});
			if (msg == ''){
				_stage.addEventListener(MouseEvent.MOUSE_DOWN, information_fade_out);
			}
		}
		
		function information_fade_out(e=undefined):void{		
			Tweener.removeTweens(information);
			Tweener.addTween(information, {alpha:0, time:.2, transition: 'linear', onComplete: information_remove});			
		}
		
		function information_remove():void{
			try{
				removeChild(information);		
			}catch(squash){ 
				// not on stage
			}				
		}
		
		function information_position(e=undefined):void{
			try{
				information.x = get_stage_width() - information.width - 5;
				information.y = get_stage_height() - information.height - 8;
			}catch(squash){ 
				// not on stage
			}
		}
		
		
		function calculate_ease_out(step:Number,steps:Number,total:Number):Number{
			/*
				defines the zoom easing, this is not related to tweening, but to 
				the levels created. linear levels make the upper levels seem
				close together so an ease out is implemented. 
			*/		
							
			var half:Number = total/2;
			var multiple:Number = step/(steps/2);
			if (multiple < 1) {
				return half*(Math.pow(multiple,2));
			}else{
				return -(half)*((--multiple)*(multiple-2)-1);	
			}					
		}
		
		function loader_progress(e:ProgressEvent):void{
			/*
				responsible for rendering the % loaded to the screen
			*/
			
			var loaded:Number = Math.floor(e.bytesLoaded / 1024)
			var total:Number = Math.floor(e.bytesTotal / 1024)
			var percent = Math.round((loaded/total) * 100)
			//trace('load:'+percent)
			load_progress.digit0.text = percent == 100 ? '1' : '';
			load_progress.digit1.text = String(percent >= 10 && percent != 100 ? Math.floor(percent/10) : 0);
			load_progress.digit2.text = String(percent % 10);
			load_progress.detail.text = String(loaded) + '/' + String(total) + 'kb';
			if (load_progress.visible == false) {
				load_progress.visible = true;
			}
			
		}
		
		
		function loader_complete(e:Event):void{
			/*
				responsible for removing the loader display
			*/
			
			load_progress.digit0.text = '1';
			load_progress.digit1.text = load_progress.digit2.text = '0';
			
			Tweener.addTween(load_progress, {alpha:0, width: load_progress.width * 1.2, height: load_progress.height * 1.2, time:.1, transition: 'linear', onComplete:function(){load_progress.visible = false;}});
			
		}
		
		function set_controls(){
			/*
				handles the key press events, sends intructions
				to the hud and directly modifies the cursor.
				TODO: cursor modificaitons in _controls?
			*/
			
			switch (true){
				case key_is_pressed(17):
					swap_cursor(new mc_icon_zoom_out());
					_controls.activate_zoom_out();	
					break;
				case key_is_pressed(16):
					swap_cursor(new mc_icon_zoom_in());
					_controls.activate_zoom_in();
					break;
				default:
					swap_cursor(new mc_icon_grabber());
					_controls.activate_drag();
					break;
			}
		}
		
		function key_direction(keycode){
			/*
				handles the movement by the up/down/left/right keys
			*/
			
			
			// limit inputs
			if (!(keycode >= 37 && keycode <= 40)){
				return;
			}
			
			var image_x:Number = image_display.x;
			var image_y:Number = image_display.y;
			var image_w:Number = image_display.width;
			var image_h:Number = image_display.height;
			var stage_w:Number = get_stage_width();
			var stage_h:Number = get_stage_height();
			var move_ratio:Number = .9;
			var end_x = image_x;
			var end_y = image_y;
						
			switch (true){				
				case key_is_pressed(37):
					trace('move left');
					end_x += stage_w * move_ratio;
					break;
				case key_is_pressed(38):
					trace('move up');
					end_y += stage_h * move_ratio;
					break;
				case key_is_pressed(39):
					trace('move right');
					end_x -= stage_w * move_ratio;
					break;
				case key_is_pressed(40):
					trace('move down');
					end_y -= stage_h * move_ratio;
					break;
				default:
					trace('unhandled exception')				
					break;
			}
			
			var inbounds = get_image_xy_inbounds(end_x, end_y, image_w, image_h)
			var inbounds_x = inbounds[0];
			var inbounds_y = inbounds[1];
			
			// allow "cubic", etc.. will transform to "Cubic"
			var titled_ease:String = _config.get_ease();
			var transition_type:String = "easeOut" + titled_ease;
			Tweener.removeTweens(image_display);			
			Tweener.addTween(image_display, {x:inbounds_x, y:inbounds_y, time:.25, transition: transition_type, onComplete: function(){ ExternalInterface.call("keenerview.handle_event", "image_zoom_complete"); } });
			
			
		}
		
		function cursor_move(e:MouseEvent):void{
			/*
				handles mouse move, custom cursor follow the mouse
			*/
			
			image_cursor.x = e.stageX;
			image_cursor.y = e.stageY;
		}
		
		function key_down(e:KeyboardEvent):void{
			/*
				keydown listener, pushes downed key to _keys
			*/
			
			trace(e.keyCode)
			
			if (_keys.indexOf(e.keyCode) == -1){ 
				_keys.push(e.keyCode);
			}
			
			set_controls();
			key_direction(e.keyCode);
			
			
		}
		
		function key_up(e:KeyboardEvent):void{
			/*
				keyup listener, removes key from _keys
			*/
			
			var i:int = _keys.indexOf(e.keyCode);		
			if (i > -1){
				_keys.splice(i, 1);
			}
			set_controls();
			
		}
				
		function key_is_pressed(key:int):Boolean{
			/*
				checks if key is down
			*/
			
			return _keys.indexOf(key) > -1;
		}
		
		
		public function get_image_xy_inbounds(image_x, image_y, image_w, image_h):Array {
			/*
				a boundary restraint, given x/y/w/h will maintain the boundarys of the stage
				plus padding. returns modified x/y.
			*/
			
			var stage_w:Number = get_stage_width();
			var stage_h:Number = get_stage_height();
			
			// due to image aspect ratio, padding isn't conclusive			 
			var adjusted_padding_x:Number = Math.max(image_padding, (stage_w - image_w)/2);
			var adjusted_padding_y:Number = Math.max(image_padding, (stage_h - image_h)/2);
			
			var inbounds_x = image_x;
			var inbounds_y = image_y;
			
			inbounds_x = Math.min(inbounds_x, adjusted_padding_x);
			inbounds_x = Math.max(inbounds_x, -(adjusted_padding_x) - image_w + stage_w);
			inbounds_y = Math.min(inbounds_y, adjusted_padding_y);
			inbounds_y = Math.max(inbounds_y, -(adjusted_padding_y) - image_h + stage_h);
			
			return [inbounds_x, inbounds_y]
			
		}
		
		public function controls_position(e = false) {
			/*
				sets the controls placement on stage
			*/
			
			_controls.x = get_stage_width() - _controls.width - 5;
			_controls.y = 5;
			
		}
		
		public function image_maintain_bounds(e = false) {
			/*
				directly modifies the image to maintain bounds, see 
				get_image_xy_inbounds for details.
			*/
			
			var image_x:Number = image_display.x;
			var image_y:Number = image_display.y;
			var image_w:Number = image_display.width;
			var image_h:Number = image_display.height;
			
			var inbounds = get_image_xy_inbounds(image_x, image_y, image_w, image_h)
			var inbounds_x = inbounds[0];
			var inbounds_y = inbounds[1];
			
			if (image_display.x != inbounds_x){image_display.x = inbounds_x}
			if (image_display.y != inbounds_y){image_display.y = inbounds_y}
			
		}
		
		
		
		public function swap_cursor(mc) {
			/*
				changes the mouse cursor
			*/
			
			while (image_cursor.numChildren) {			   
				image_cursor.removeChildAt(0);
			}
			// DOUBLE_CLICK ISSUES adding mc, the click is canceled!!  
			image_cursor.addChild(mc);
		}
						
		public function image_roll_over(e:MouseEvent) {
			/*
				changes the mouse cursor from default to custom
			*/
			
			Mouse.hide();
			image_cursor.visible = true;
		}
		
		public function stage_exit(e:Event) {
			/*
				stage exit handler, restores default mouse and ends drag
			*/
			
			ExternalInterface.call("keenerview.handle_event", "stage_exit");
			
			trace('stage_exit')
			Mouse.show();
			image_cursor.visible = false;
			swap_cursor(new mc_icon_grabber());
			image_display.stopDrag();			
		}
		
		public function image_roll_out(e:MouseEvent) {
			/*
				image roll out handler, restores default mouse and ends drag
				if the mouse is still pressed, it will maintain the drag.
			*/
			
			if (e.buttonDown == false){
				Mouse.show();
				image_cursor.visible = false;
				image_drag_stop(e);
			}
		}
		
		
		public function image_drag_start(e:MouseEvent) {
			/*
				drag handler, implements boundary
			*/
			
			// x, y, w, h
			if (_controls.hud_current == 'drag'){
				var r:Rectangle = new Rectangle(-(image_display.width) + _stage.stageWidth - image_padding, -(image_display.height) + _stage.stageHeight - image_padding, image_display.width - _stage.stageWidth + image_padding * 2, image_display.height - _stage.stageHeight  + image_padding * 2);
				image_display.startDrag(false, r);
				/*
				var swap_timer:Timer = new Timer(200, 1); // delay 1 second, repeats once
				swap_timer.start();
				swap_timer.addEventListener(TimerEvent.TIMER_COMPLETE, function():void{ swap_cursor(new mc_icon_grabbed());} );
				*/
				swap_cursor(new mc_icon_grabbed());
			}
			
		}
		
		public function image_drag_stop(e:MouseEvent) {
			/*
				drop handler
			*/
			trace('mouse up')
			swap_cursor(new mc_icon_grabber());
			image_display.stopDrag();
			
		}
		
		public function image_zoom(zoom_level:int, zoom_center:Array):void {
			/*
				given zoom level - zoom. all zooms are implemented here.
			*/
			
			if (zoom_level != image_zoom_current){
				zoom_level = Math.max(0, zoom_level)
				zoom_level = Math.min(image_zoom_levels.length - 1, zoom_level)
				image_zoom_current = zoom_level;
				var l = image_zoom_levels[image_zoom_current]
				var w = l[0]
				var h = l[1]
				set_image_dimensions(w, h, zoom_center, true);
				_controls.zoom_slider_component.value = zoom_level;
			}
		}
		
		public function image_wheel_zoom(e:MouseEvent):void {
			/*
				wheel mouse listener - maps to zoom
			*/
			
			// firefox issues
			var current_time:Number = new Date().getTime();
			if (current_time - wheel_mouse_lock_time < 10){
				return;	
			}
			
			wheel_mouse_lock_time = current_time;	
			
			
			e.stopPropagation();
			
			ExternalInterface.call("keenerview.handle_event", "image_zoom_start");

			var wheel_delta = e.delta;			
			if (wheel_delta > 0){
				image_zoom(image_zoom_current + 1, [])
			}else{
				image_zoom(image_zoom_current - 1, [])
			}
			information_fade_out();
		}
		
		public function image_click_zoom(e:MouseEvent):void {
			/*
				double click handler - maps to zoom, all the way in or all 
				the way out
			*/
			trace('double click')
			var zoom_change:int = (_controls.hud_current == 'zoom_out' || image_zoom_current == image_zoom_levels.length -1) ? image_zoom_level_count * -1 : image_zoom_level_count;
			image_zoom(image_zoom_current + zoom_change, [e.stageX,e.stageY]);
		}
		
		public function image_click(e:MouseEvent):void {						
			/*
				click handler - maps to zoom if zoom is the current control, 
				increments/decrements one level
			*/
			
			if (_controls.hud_current == 'zoom_out'){
				image_zoom(image_zoom_current -1, [e.stageX,e.stageY]);
			}else if (_controls.hud_current == 'zoom_in'){
				image_zoom(image_zoom_current +1, [e.stageX,e.stageY]);
			}
		}
		
		
		public function get_stage_width():Number {
			/*
				stage dimension getter
			*/
			
			return _stage.stageWidth;
		}
		
		public function get_stage_height():Number {
			/*
				stage dimension getter
			*/
			
			return _stage.stageHeight;
		}
		
		public function get_stage_ratio():Number {
			/*
				stage aspect ratio getter
			*/
			
			return get_stage_width()/get_stage_height();
		}
		
		public function get_image_ratio():Number {
			/*
				image aspect ratio getter
			*/
			
			return image_display.width/image_display.height;
		}
		
		public function set_image_dimensions(w:Number, h:Number, center:Array, tween:Boolean):void {
			/*
				given width and height, will set the image size
				@center defaults to stage center, but can be passed mouse coordinates
				@tween determines whether to tween to the new dimensions or not
			*/
			
			var center_x:Number;
			var center_y:Number;
				
			// preserve center
			var image_x:Number = image_display.x;
			var image_y:Number = image_display.y;
			var image_w:Number = image_display.width;
			var image_h:Number = image_display.height;
			var stage_w:Number = get_stage_width();
			var stage_h:Number = get_stage_height();
			trace("image_x: "+image_x + "\nimage_y: "+image_y)
			
			if (center.length == 2){
				// use the provided stage centering - likely from mouse event
				var stage_center_x = center[0];
				var stage_center_y = center[1];
				center_x = ((stage_center_x/stage_w * stage_w) + -(image_x));
				center_y = ((stage_center_y/stage_h * stage_h) + -(image_y));
			}else{			
				// natural center is the currently centered pixel on stage
				center_x = (stage_w/2 + -(image_x));
				center_y = (stage_h/2 + -(image_y));
			}
						
			var center_ratio_x:Number = center_x/image_w;
			var center_ratio_y:Number = center_y/image_h;
			trace("center_x: "+center_x + "\ncenter_y: "+center_y)
			trace("center_ratio_x: "+center_ratio_x + "\ncenter_ratio_y: "+center_ratio_y)
						
			var end_x = -(w * center_ratio_x) + stage_w/2;
			var end_y = -(h * center_ratio_y) + stage_h/2;
			
			var inbounds = get_image_xy_inbounds(end_x, end_y, w, h)
			var inbounds_x = inbounds[0];
			var inbounds_y = inbounds[1];
			
			var shadow_zoom:Number = 40 / (image_width/w);
			
			// prevent simultaneous tweens
			Tweener.removeTweens(image_display);
			
			if (tween == true){
				// allow "cubic", etc.. will transform to "Cubic"
				var titled_ease:String = _config.get_ease();
				var transition_type:String = w > image_w ? "easeIn" + titled_ease : "easeOut" + titled_ease;
				if (image_display.filters.length > 0){
					Tweener.addTween(image_display, {x:inbounds_x, y:inbounds_y, width:w, height:h, _DropShadow_blurX:shadow_zoom, _DropShadow_blurY:shadow_zoom, time:.25, transition: transition_type, onComplete: function(){ ExternalInterface.call("keenerview.handle_event", "image_zoom_complete");} });
				}else{
					Tweener.addTween(image_display, {x:inbounds_x, y:inbounds_y, width:w, height:h, time:.25, transition: transition_type, onComplete: function(){ ExternalInterface.call("keenerview.handle_event", "image_zoom_complete"); } });
				}
			}else{
				image_display.width = w;
				image_display.height = h;
				image_display.x = end_x;
				image_display.y = end_y;
			}			
			
		}
		
		public function set_image_to_center():void {		
			/*
				simple centering function for initialization
			*/	
			
			image_display.x = (get_stage_width() - image_display.width) / 2;
			image_display.y = (get_stage_height() - image_display.height) / 2;
		}
		
		public function reset_image():void {
			/*
				simple reset function for initialization
			*/
			var w:Number;
			var h:Number;
			
			// shrink to fit window
			var image_ratio = get_image_ratio();
			var stage_ratio = get_stage_ratio();
			var stage_width = get_stage_width();
			var stage_height = get_stage_height();
			
			if (image_ratio >= stage_ratio){
				w = stage_width - (image_padding * 2);
				h = image_height * (w/image_width);				
			}else{
				h = stage_height - (image_padding * 2);
				w = image_width * (h/image_height);				
			}			
			set_image_dimensions(w, h, [], false);
			set_image_to_center();
		} 
		
		public function load_image():void { 
			/*
				handles request for image and subsequent viewer initialization based on
				image properties. zoom levels are determined from image dimensions and
				event handlers are attached to the image for dragging and zooming
			*/		
			
			var image_loader:Loader = new Loader();
			
			image_loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, loader_progress);
		  	image_loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loader_complete);
		  	
			var image_request:URLRequest = new URLRequest(request_url);
			
			image_loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, function(e:Event):void {  
					trace('failed to load image');
					information_fade_in('failed to load image.\n'+request_url)
					io_failure = true;
					loop_callback(); 
				}
			);
			
			image_loader.contentLoaderInfo.addEventListener(Event.COMPLETE, function(e:Event):void {				
				
				ExternalInterface.call("keenerview.handle_event", "image_load_start");
								
				information_fade_in();
				
				var bitmap = Bitmap(image_loader.content); //get the loaders content as a bitmap
				bitmap.smoothing = true; //turn on smoothing
				
				image_display.addChild(bitmap);
				
				image_width = image_display.width;
				image_height = image_display.height;
				image_zoom_level_count = _config.image_zoom_level_count > 0 ? _config.image_zoom_level_count : 2 + Math.ceil(image_width/3000);
				
				// http://www.maxtherocket.com/blog/flash-as3-double-click-not-working/
				//image_display.mouseChildren = false;
				image_display.doubleClickEnabled = true;				
				image_display.addEventListener(MouseEvent.DOUBLE_CLICK, image_click_zoom);
				
				
				image_display.addEventListener(MouseEvent.CLICK, image_click);
				
				image_display.addEventListener(MouseEvent.MOUSE_DOWN, image_drag_start);
				image_display.addEventListener(MouseEvent.MOUSE_UP, image_drag_stop);
				
				// double up due to bubbling conflict with stage MOUSE_MOVE listener
				_stage.addEventListener(MouseEvent.MOUSE_UP, image_drag_stop);
				
				
				// outside of stage, don't continue drag
				_stage.addEventListener(Event.MOUSE_LEAVE, stage_exit);
				
				image_display.addEventListener(MouseEvent.MOUSE_OUT, image_roll_out);				
				image_display.addEventListener(MouseEvent.MOUSE_OVER, image_roll_over);				
				image_display.addEventListener(MouseEvent.MOUSE_WHEEL, image_wheel_zoom);
				
				reset_image();
				
				var w:Number = image_display.width;
				var h:Number = image_display.height;
				if (w*h <= 7000000){
					var shadow_zoom = 40 / (image_width/w)
					var dropShadow:DropShadowFilter = new DropShadowFilter();
					dropShadow.color = 0x000000;
					dropShadow.blurX = shadow_zoom;
					dropShadow.blurY = shadow_zoom;
					dropShadow.angle = 65;
					dropShadow.alpha = 1;
					dropShadow.distance = 3;
					dropShadow.quality = BitmapFilterQuality.LOW;
					image_display.filters = [dropShadow];
				}else{
					image_display.filters = [];
				}
				
				
				Tweener.addTween(_controls, {alpha:1, time:.3, transition: 'linear'});
				_stage.addChild(image_cursor);
				// mouse enabled cursor interferes with image display DOUBLE_CLICK
				image_cursor.mouseChildren = false;
				image_cursor.mouseEnabled = false;
				
				// establish zoom levels
				image_zoom_levels = [];
				var start_w = image_display.width;
				var start_h = image_display.height;
				
				for (var i=0; i<image_zoom_level_count; i++){
					var ease_width = calculate_ease_out(i,image_zoom_level_count, image_width - start_w)
					var ease_height = calculate_ease_out(i,image_zoom_level_count, image_height - start_h)
					trace(ease_width)
					image_zoom_levels.push([ease_width + start_w,ease_height + start_h])	
				}
				
				_controls.zoom_slider_set_size(image_zoom_level_count)
				trace(image_zoom_levels)
				
				loaded = true;
				loop_callback();
				
				ExternalInterface.call("keenerview.handle_event", "image_load_complete");
				
				image_display.addChild(highlights_display);
				_ocr.handle_ocr();
					
			});
			
			trace('loading image: '+request_url);
			image_loader.load(image_request);
			
		}
		
	}
	
}
